package com.nowcoder.community.util;

import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Insert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.xml.stream.events.Characters;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

@Component
public class SensitiveFilter {
    private static final Logger logger = LoggerFactory.getLogger(SensitiveFilter.class);
    private static final String REPLACEMENT="***"; //替换敏感词的常量
    private class TrieNode{
        private boolean isKeywordEnd= false;//关键词结束标识
        private Map<Character,TrieNode>subNodes=new HashMap<>();
        //子结点(key下级字符，val是下级节点)
        public boolean isKeywordEnd(){return isKeywordEnd;}
        public void setKeywordEnd(boolean KeywordEnd){isKeywordEnd=KeywordEnd;}
        //设置关键字结束符
        public void addSubNode(Character c,TrieNode node){
            subNodes.put(c,node);
        }
        //添加子结点
        public TrieNode getSubNode(Character c){return subNodes.get(c);}
        //获取子结点

    }
    //前缀树--数据结构
    private TrieNode rootNode=new TrieNode();//根节点
    private void addKeyword(String keyword){
        TrieNode tmp=rootNode;
        int len=keyword.length();
        for(int i=0;i<len;i++){

            //获取该字符
            char c= keyword.charAt(i);
            TrieNode subNode=tmp.getSubNode(c);

            //如果子结点为空，则初始化字节点，并将其加入子结点树
            if(subNode==null){
                subNode=new TrieNode();
                tmp.addSubNode(c,subNode);
            }
            tmp=subNode;//存在即进入子树

            if(i==len-1)tmp.setKeywordEnd(true);//到达终点
        }
    }
    //为前缀树添加敏感词树
    private boolean isSymbol(Character c) {
        // 0x2E80~0x9FFF 是东亚文字范围
        return !CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9FFF);
    }
    //判断是否字符
    @PostConstruct//将敏感词添加到前缀树上
    public void init() {
        try(
            //获取输入流资源
            InputStream is=this.getClass().getClassLoader().getResourceAsStream("sensitive-words.txt");
            BufferedReader reader=new BufferedReader(new InputStreamReader(is));
        ){
            String keyWord;
            while((keyWord=reader.readLine())!=null)  this.addKeyword(keyWord);
        }catch (IOException e){
            logger.error("加载敏感词文件失败: " + e.getMessage());
        }
    }
    public String filter(String text){
        if (StringUtils.isBlank(text)) return null;//如果文本为空
        
        TrieNode tmp=rootNode;//指针1
        int begin=0,position=0;//指针2,3
        StringBuilder result = new StringBuilder();//结果字符串
        int len=text.length();//待过滤字符串长度
        
        while(position<len){
            //获取该位字符
            char c=text.charAt(position);
            
            //跳过符号--符号不可能是违规词
            if(isSymbol(c)){
                //若指针1处在根节点，将该符号记入结果，并且让2指针向下走一步
                if(tmp==rootNode){
                    result.append(c);
                    begin++;
                }
                //无论符号是在开头还是中间，指针3都往下走一步
                position++;
                continue;
            }
            
            //获取下级节点
            tmp=tmp.getSubNode(c);
            if(tmp==null){
                //以begin开头的字符串不是敏感词
                result.append(text.charAt(begin));
                //进入下一位置
                position=++begin;
                //重新指向根节点，为了下次搜寻
                tmp=rootNode;
            }
            else if(tmp.isKeywordEnd()){
                //发现敏感词，需要将begin~position中的字符串代替
                result.append(REPLACEMENT);
                
                //进入下一个位置
                begin=++position;
                
                //将根节点回归
                tmp=rootNode;
            }
            else {
                //检查下一个
                position++;
            }
        }
        //将最后一批字符串加入结果
        result.append(text.substring(begin));
        return result.toString();
        
    }//过滤敏感词--传入待过滤的文本--返回过滤后的文本
}
